package cn.infomany.controller;

import cn.infomany.SyntaxErrorListener;
import cn.infomany.bean.ErrorInfo;
import cn.infomany.code.CodeHighlightListener;
import cn.infomany.code.RangesVisitor;
import cn.infomany.code.generate.CodeLexer;
import cn.infomany.code.generate.CodeParser;
import cn.infomany.control.MyCodeArea;
import de.felixroske.jfxsupport.FXMLController;
import javafx.application.Platform;
import javafx.event.ActionEvent;
import javafx.fxml.Initializable;
import javafx.scene.input.KeyCode;
import javafx.scene.input.KeyEvent;
import org.antlr.v4.runtime.CharStream;
import org.antlr.v4.runtime.CharStreams;
import org.antlr.v4.runtime.CommonTokenStream;
import org.antlr.v4.runtime.tree.ParseTree;
import org.antlr.v4.runtime.tree.ParseTreeWalker;
import org.fxmisc.richtext.CodeArea;
import org.fxmisc.richtext.model.StyleSpans;
import org.fxmisc.richtext.model.StyleSpansBuilder;

import java.net.URL;
import java.time.Duration;
import java.util.Collection;
import java.util.Collections;
import java.util.Objects;
import java.util.ResourceBundle;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

@FXMLController
public class DemoController implements Initializable {


    public MyCodeArea codeArea;
    public CodeArea console;


    @Override
    public void initialize(URL location, ResourceBundle resources) {
        codeArea.multiPlainChanges()
                .successionEnds(Duration.ofMillis(500))
                .subscribe(ignore -> {

                    StyleSpans<? extends Collection<String>> styleSpans = computeHighlighting(codeArea.getText());
                    if (!Objects.isNull(styleSpans))
                        codeArea.setStyleSpans(0, styleSpans);
                });

        final Pattern whiteSpace = Pattern.compile("^\\s+");
        codeArea.addEventHandler(KeyEvent.KEY_PRESSED, KE ->
        {
            if (KE.getCode() == KeyCode.ENTER) {
                int caretPosition = codeArea.getCaretPosition();
                int currentParagraph = codeArea.getCurrentParagraph();
                Matcher m0 = whiteSpace.matcher(codeArea.getParagraph(currentParagraph - 1).getSegments().get(0));
                if (m0.find()) Platform.runLater(() -> codeArea.insertText(caretPosition, m0.group()));
            }
        });

    }

    public String generate(ActionEvent actionEvent) {
        // 新建一个CharStreams，从标准输入读取数据
        CharStream input = CharStreams.fromString(codeArea.getText());
        // 新建一个词法符号的缓冲区，用于存储词法分析器将生成的词法符号
        CodeLexer lexer = new CodeLexer(input);
        // 新建一个词法符号的缓冲区，用于存储词法分析器将生成的词法符号
        CommonTokenStream tokens = new CommonTokenStream(lexer);
        // 新建一个语法分析器，处理词法符号缓冲区中的内容
        CodeParser parser = new CodeParser(tokens);
        // 针对init规则，开始语法分析
        ParseTree tree = parser.range();

        RangesVisitor rangesVisitor = new RangesVisitor();
        Object visit = rangesVisitor.visit(tree);
        return visit.toString();
    }

    private StyleSpans<? extends Collection<String>> computeHighlighting(String text) {
        // 新建一个CharStreams，从标准输入读取数据
        CharStream input = CharStreams.fromString(text);
        // 新建一个词法符号的缓冲区，用于存储词法分析器将生成的词法符号
        CodeLexer lexer = new CodeLexer(input);
        // 新建一个词法符号的缓冲区，用于存储词法分析器将生成的词法符号
        CommonTokenStream tokens = new CommonTokenStream(lexer);
        // 新建一个语法分析器，处理词法符号缓冲区中的内容
        CodeParser parser = new CodeParser(tokens);

        SyntaxErrorListener syntaxErrorListener = new SyntaxErrorListener();
        parser.removeErrorListeners();
        parser.addErrorListener(syntaxErrorListener);


        StyleSpansBuilder<Collection<String>> spansBuilder = new StyleSpansBuilder<>();
        CodeHighlightListener codeHighlightListener = new CodeHighlightListener(spansBuilder);

        ParseTreeWalker walker = new ParseTreeWalker();

        walker.walk(codeHighlightListener, parser.range());

        if (syntaxErrorListener.isErrorOccurred()) {
            ErrorInfo errorInfo = syntaxErrorListener.getErrorInfo();

            String generate = String.format("在第%s行,第%s列出现错误", errorInfo.getLine(),
                    errorInfo.getCharPositionInLine());
            console.replaceText(generate);
            console.setStyle(0, generate.length(), Collections.singleton("error"));
            return null;
        }

        console.replaceText(generate(null));
        return spansBuilder.create();
    }


}